//
// Created by 张易生 on 2022/1/12.
//


#include "ZYSFFmpeg.h"

ZYSFFmpeg::ZYSFFmpeg(ZYSPlayStatus *playStatus, ZYSCallJava *callJava, const char *url) {
    this->playStatus = playStatus;
    this->callJava = callJava;
    this->url = url;
    //初始化的互斥锁
    pthread_mutex_init(&init_mutex, NULL);
    //seek的互斥锁
    pthread_mutex_init(&seek_mutex, NULL);
}

void *initFFmpegDeCode(void *data) {
    ZYSFFmpeg *zysFFmpeg = (ZYSFFmpeg *) data;
    if (zysFFmpeg != nullptr) {
        zysFFmpeg->decodeFFmpegThread();
    }

    pthread_exit(&zysFFmpeg->decodeThread);
}

void ZYSFFmpeg::prepared() {
    //创建子线程
    pthread_create(&decodeThread, nullptr, initFFmpegDeCode, this);
}

//在子线程初始化
void ZYSFFmpeg::decodeFFmpegThread() {
    pthread_mutex_lock(&init_mutex);
    av_register_all();
    avformat_network_init();

    avFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&avFormatCtx, url, nullptr, nullptr) != 0) {
        if (LOG_DEBUG) {
            LOGE("can not open url :%s", url);
        }
        return;
    }
    if (avformat_find_stream_info(avFormatCtx, nullptr) < 0) {
        if (LOG_DEBUG) {
            LOGE("can not find streams from %s", url);
        }
        return;
    }
    for (int i = 0; i < avFormatCtx->nb_streams; i++) {
        //得到音频流
        if (avFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            if (audioPlayer == nullptr) {
                audioPlayer = new ZYSAudioPlayer(playStatus,
                                                 avFormatCtx->streams[i]->codecpar->sample_rate,
                                                 callJava);
                audioPlayer->streamIndex = i;
//                 pFormatCtx->duration  微妙 转换成秒
                audioPlayer->duration = avFormatCtx->duration / AV_TIME_BASE;
                audioPlayer->time_base = avFormatCtx->streams[i]->time_base;
                audioPlayer->codecpar = avFormatCtx->streams[i]->codecpar;
                duration = audioPlayer->duration;
            }
        } else if (avFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            if (videoPlayer == nullptr) {
                videoPlayer = new ZYSVideoPlayer(playStatus, callJava);
                videoPlayer->streamIndex = i;
                videoPlayer->codecpar = avFormatCtx->streams[i]->codecpar;
//                 pFormatCtx->duration  微妙 转换成秒
                videoPlayer->duration = avFormatCtx->duration / AV_TIME_BASE;

                videoPlayer->time_base = avFormatCtx->streams[i]->time_base;
                //分子
                int num = avFormatCtx->streams[i]->avg_frame_rate.num;
                //分母
                int den = avFormatCtx->streams[i]->avg_frame_rate.den;

                if (num != 0 && den != 0) {
                    //计算fps
                    double fps = num * 1.0 / den;
//                    LOGE("---fps %f", fps)
                    //计算1帧需要多少秒
                    videoPlayer->defaultDelayTime = 1.0 / fps;
//                    LOGE("---videoPlayer->defaultDelayTime= %f", videoPlayer->defaultDelayTime)
                }

                duration = videoPlayer->duration;
            }
        }
    }

    if (audioPlayer == nullptr && videoPlayer == nullptr) {
        if (LOG_DEBUG) {
            LOGE("can not find audio streams from %s", url);
        }
        //需要释放
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }
    if (audioPlayer != nullptr) {
        getCodecContext(1, audioPlayer->codecpar, &audioPlayer->avCodecContext);
    }
    if (videoPlayer != nullptr) {
        getCodecContext(2, videoPlayer->codecpar, &videoPlayer->avCodecContext);
    }
    if (audioPlayer != nullptr && videoPlayer != nullptr) {
        //创建音频解码器
        if (audioPlayer->duration != 0 && videoPlayer->duration != 0) {
            if (audioPlayer->duration <= videoPlayer->duration) {
                duration = audioPlayer->duration;
            } else {
                duration = videoPlayer->duration;
            }
        } else if (audioPlayer->duration != 0) {
            duration = audioPlayer->duration;
            LOGE("audioPlayer->duration = %d", duration)
        } else {
            duration = videoPlayer->duration;
            LOGE("videoPlayer->duration = %d", duration)
        }

    }

    callJava->onCallPrepared(CHILD_THREAD, duration);
    pthread_mutex_unlock(&init_mutex);
}

void *startAVDecode_c(void *data) {
    ZYSFFmpeg *zysFFmpeg = (ZYSFFmpeg *) data;
    if (zysFFmpeg != nullptr) {
//        zysFFmpeg->startVideoDecode_cpp();
        zysFFmpeg->startAVDecode_cpp();
    }
    pthread_exit(&zysFFmpeg->decodeThread);
}


void ZYSFFmpeg::startAVDecode_cpp() {
    while (playStatus != nullptr && !playStatus->exit) {
        //        seek  耗时
        if (videoPlayer == nullptr && audioPlayer == nullptr) {
            break;
        }
        if (playStatus->exit) {
            break;
        }
        if (playStatus->seek) {
            continue;
        }
        if (audioPlayer != nullptr) {
            if (audioPlayer->queue->getQueueSize() > 40) {
                continue;
            }
            LOGE("---audioPlayer->queue->getQueueSize()= %d", audioPlayer->queue->getQueueSize())
        }
        if (videoPlayer != nullptr) {
            LOGE("---videoPlayer->queue->getQueueSize()= %d", videoPlayer->queue->getQueueSize())
        }
        AVPacket *avPacket = av_packet_alloc();
        if (av_read_frame(avFormatCtx, avPacket) == 0) {
            if (audioPlayer != nullptr && avPacket->stream_index == audioPlayer->streamIndex) {
                audioPlayer->queue->putAVPacket(avPacket);
            } else if (videoPlayer != nullptr &&
                       avPacket->stream_index == videoPlayer->streamIndex) {
                videoPlayer->queue->putAVPacket(avPacket);
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
            }
        } else {
            av_packet_free(&avPacket);
            av_free(avPacket);
            while (playStatus != nullptr && !playStatus->exit) {
                if (audioPlayer != nullptr && audioPlayer->queue->getQueueSize() > 0) {
                    continue;
                } else if (videoPlayer != nullptr && videoPlayer->queue->getQueueSize() > 0) {
                    continue;
                } else {
                    playStatus->exit = true;
                    break;
                }
            }
        }

    }

    if (LOG_DEBUG) {
        LOGD("解码完成");
    }

}


void ZYSFFmpeg::startDecode() {

    if (audioPlayer == nullptr && videoPlayer == nullptr) {
        if (LOG_DEBUG) {
            LOGE("audio and video is null");
            return;
        }
    }
    if (audioPlayer != nullptr) {
        audioPlayer->play();
    }
    if (videoPlayer != nullptr) {
        videoPlayer->play();
        if (audioPlayer != nullptr) {
            videoPlayer->audio = audioPlayer;
        }
    }

    pthread_create(&decodeThread, nullptr, startAVDecode_c, this);
}


void ZYSFFmpeg::releaseFormatContext() {
    if (avFormatCtx) {
        avformat_free_context(avFormatCtx);
        avFormatCtx = nullptr;
    }
}

void ZYSFFmpeg::pause() {
    if (audioPlayer != nullptr) {
        audioPlayer->pause();
    }
    if (videoPlayer != nullptr) {
        videoPlayer->pause();
    }
}

void ZYSFFmpeg::resume() {
    if (audioPlayer != nullptr) {
        audioPlayer->resume();
    }
    if (videoPlayer != nullptr) {
        videoPlayer->resume();
    }
}

void ZYSFFmpeg::setMute(int mute) {
    if (audioPlayer != nullptr) {
        audioPlayer->setMute(mute);
    }
}

void ZYSFFmpeg::setVolume(int percent) {
    if (audioPlayer != nullptr) {
        audioPlayer->setVolume(percent);
    }
}

void ZYSFFmpeg::seek(int64_t parent) {
    if (duration <= 0) {
        return;
    }
    if (parent >= 0 && parent <= duration) {
        playStatus->seek = true;
        //           记得在ZYSFFmpeg()中初始化互斥锁seek_mutex
        pthread_mutex_lock(&seek_mutex);
        int64_t rel = parent * AV_TIME_BASE;
        LOGE("rel time %d", parent);
        avformat_seek_file(avFormatCtx, -1, INT64_MIN, rel, INT64_MAX, 0);
        if (audioPlayer != nullptr) {
            playStatus->seek = true;
            audioPlayer->queue->clearAVPacket();
            audioPlayer->clock = 0;
            audioPlayer->last_time = 0;
        }
        if (videoPlayer != nullptr) {
            videoPlayer->queue->clearAVPacket();
            pthread_mutex_lock(&videoPlayer->codecMutex);
            avcodec_flush_buffers(videoPlayer->avCodecContext);
            pthread_mutex_unlock(&videoPlayer->codecMutex);
        }
        pthread_mutex_unlock(&seek_mutex);
        playStatus->seek = false;
    }

}

void ZYSFFmpeg::release() {
    playStatus->exit = true;
    pthread_mutex_lock(&init_mutex);

    if (audioPlayer != nullptr) {
        audioPlayer->release();
        audioPlayer = nullptr;
    }
    if (videoPlayer != nullptr) {
        videoPlayer->release();
        videoPlayer = nullptr;
    }
    if (avFormatCtx != nullptr) {
        avformat_close_input(&avFormatCtx);
        avformat_free_context(avFormatCtx);
        avFormatCtx = nullptr;
    }

    if (callJava != nullptr) {
        delete (callJava);
        callJava = nullptr;
    }

    if (playStatus != nullptr) {
        delete (playStatus);
        playStatus = nullptr;
    }

    int sleepCount = 0;

    while (!exit) {
        if (sleepCount > 1000) {
            exit = true;
        }
        sleepCount++;
        //暂停10毫秒
        av_usleep(1000 * 10);
    }

    pthread_mutex_unlock(&init_mutex);
}


ZYSFFmpeg::~ZYSFFmpeg() {
    pthread_mutex_destroy(&init_mutex);
    pthread_mutex_destroy(&seek_mutex);
}

//倍速
void ZYSFFmpeg::setSpeed(float speed) {
    if (audioPlayer != nullptr) {
        audioPlayer->setSpeed(speed);
    }
}

//变音调
void ZYSFFmpeg::setPitch(float pitch) {
    if (audioPlayer != nullptr) {
        audioPlayer->setPitch(pitch);
    }
}

int
ZYSFFmpeg::getCodecContext(int type, AVCodecParameters *codecpar,
                           AVCodecContext **avCodecContext) {
    AVCodec *dec = avcodec_find_decoder(codecpar->codec_id);
    if (!dec) {
        if (LOG_DEBUG) {
            LOGE("can not find decoder type %d", type);
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }
    //得到音频解码器上下文
    *avCodecContext = avcodec_alloc_context3(dec);
    if (!avCodecContext) {
        if (LOG_DEBUG) {
            LOGE("can not alloc new decodecctx type %d", type);
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }

    if (avcodec_parameters_to_context(*avCodecContext, codecpar) < 0) {
        if (LOG_DEBUG) {
            LOGE("can not fill decodecctx type %d", type);
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }

    if (avcodec_open2(*avCodecContext, dec, 0) != 0) {
        if (LOG_DEBUG) {
            LOGE("cant not open strames type %d", type);
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }
    return 0;
}








